/**
Nectarine Utility Project is a software interface for the web system 'demovibes'
Copyright (C) 2010  Alexandre LAURENT

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

website: http://code.google.com/p/nectarine-utility-project/
e-mail: lw.demoscene@gmail.com
**/

#include "WebClient.h"

#include <QObject>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetworkCookieJar>
#include <QNetworkProxy>
#include <QUrl>
#include <QByteArray>

#include <QDebug>

#include "webutils.h"
#include "webdebug.h"
#include "../globals.h"

WebClient :: WebClient(const QUrl& rootSiteUrl, QObject* parent /*=0*/)
    :QNetworkAccessManager(parent),basicRequest(rootSiteUrl)
{
    // Creation of the basic request
    basicRequest.setRawHeader("User-Agent", USER_AGENT_NAME);

    // Creation of the cookies manager
    pCookies = new QNetworkCookieJar(this);

    this->setCookieJar(pCookies);

    // Connection to the normal replyFinished function
    connect(this, SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*)));

    qDebug() << "WebClient created";
}

WebClient :: ~WebClient(void)
{
    qDebug() << "WebClient deleted";
}

void WebClient :: requestQueue(void)
{
    // Creation of the URL
    QUrl queueUrl(basicRequest.url().toString() + QString(QUEUE_PATH));
    QNetworkRequest queueRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient getQueue " << queueUrl;

    // Loading of the URL in the request
    queueRequest.setUrl(queueUrl);
    // Set an additionnal attribute to recognise the goal of the request
    queueRequest.setAttribute(QNetworkRequest::User,RT_QueueRead);

    pReply = this->get(queueRequest);
}

void WebClient :: requestOneliner(void)
{
    // Creation of the URL
    QUrl queueUrl(basicRequest.url().toString() + QString(ONELINER_PATH));
    QNetworkRequest onelinerRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestOneliner " << queueUrl;

    // Loading of the URL in the request
    onelinerRequest.setUrl(queueUrl);
    // Set an additionnal attribute to recognise the goal of the request
    onelinerRequest.setAttribute(QNetworkRequest::User,RT_OnelinerRead);

    pReply = this->get(onelinerRequest);
}

void WebClient :: requestNews(const NewsRequestReason reason/*=NRR_News*/)
{
    // Creation of the URL
    QUrl newsUrl(basicRequest.url().toString() + QString(NEWS_PATH));
    QNetworkRequest newsRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestNews() " << newsUrl << "(" << reason << ")";

    // Loading of the URL in the request
    newsRequest.setUrl(newsUrl);
    // Set an additionnal attribute to recognise the goal of the request
    switch ( reason )
    {
        case NRR_News:
            newsRequest.setAttribute(QNetworkRequest::User,RT_NewsRead);
            break;
        case NRR_UserOnliner:
            newsRequest.setAttribute(QNetworkRequest::User,RT_UsersOnlineRead);
            break;
        case NRR_Inbox:
            newsRequest.setAttribute(QNetworkRequest::User,RT_InboxRead);
            break;
    }

    pReply = this->get(newsRequest);
}

void WebClient :: requestStream()
{
    // Creation of the URL
    QUrl streamUrl(basicRequest.url().toString() + QString(STREAM_PATH));
    QNetworkRequest streamRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestStream() " << streamUrl;

    // Loading of the URL in the request
    streamRequest.setUrl(streamUrl);
    // Set an additionnal attribute to recognise the goal of the request
    streamRequest.setAttribute(QNetworkRequest::User,RT_StreamRead);

    pReply = this->get(streamRequest);
}

void WebClient :: requestFavoritePage(const QString& username, const bool isUsersFav)
{
    // Creation of the URL
    QUrl favoriteUrl(basicRequest.url().toString() + "/demovibes/user/" + username + QString(FAVORITE_PATH));
    QNetworkRequest favoriteRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestFavoritePage() " << favoriteUrl << " User: " << username << " isUser: " << isUsersFav ;

    // Loading of the URL in the request
    favoriteRequest.setUrl(favoriteUrl);
    // Set an additionnal attribute to recognise the goal of the request
    if (isUsersFav == true)
    {
        favoriteRequest.setAttribute(QNetworkRequest::User,RT_UserFavoritesRead);
    }
    else
    {
        favoriteRequest.setAttribute(QNetworkRequest::User,RT_FavoritesRead);
    }

    pReply = this->get(favoriteRequest);
}

void WebClient :: requestFavoritePage(const QString& username, const bool isUsersFav, const unsigned int pageNumber)
{
    // Creation of the URL
    QUrl favoriteUrl(basicRequest.url().toString() + "/demovibes/user/" + username + QString(FAVORITE_PATH) + QString("?&p=%1").arg(pageNumber));
    QNetworkRequest favoriteRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestFavoritePage() " << favoriteUrl << " User: " << username << " isUser: " << isUsersFav ;

    // Loading of the URL in the request
    favoriteRequest.setUrl(favoriteUrl);
    // Set an additionnal attribute to recognise the goal of the request
    if (isUsersFav == true)
    {
        favoriteRequest.setAttribute(QNetworkRequest::User,RT_UserPaginatedFavoritesRead);
    }
    else
    {
        favoriteRequest.setAttribute(QNetworkRequest::User,RT_PaginatedFavoritesRead);
    }

    pReply = this->get(favoriteRequest);
}

void WebClient :: requestSongPage(const int id)
{
    // Creation of the URL
    QUrl songUrl(basicRequest.url().toString() +QString(SONG_PATH) + QString("%1").arg(id) + QString("/"));
    QNetworkRequest songRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestSongPage() " << songUrl;

    // Loading of the URL in the request
    songRequest.setUrl(songUrl);
    // Set an additionnal attribute to recognise the goal of the request
    songRequest.setAttribute(QNetworkRequest::User,RT_SongRead);

    pReply = this->get(songRequest);
}

void WebClient :: requestArtistPage(const int id)
{
    // Creation of the URL
    QUrl artistUrl(basicRequest.url().toString() +QString(ARTIST_PATH) + QString("%1").arg(id) + QString("/"));
    QNetworkRequest artistRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestArtistPage() " << artistUrl;

    // Loading of the URL in the request
    artistRequest.setUrl(artistUrl);
    // Set an additionnal attribute to recognise the goal of the request
    artistRequest.setAttribute(QNetworkRequest::User,RT_ArtistRead);

    pReply = this->get(artistRequest);
}

void WebClient :: requestGroupPage(const unsigned int id)
{
    // Creation of the URL
    QUrl groupUrl(basicRequest.url().toString() +QString(GROUP_PATH) + QString("%1").arg(id) + QString("/"));
    QNetworkRequest groupRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestGroupPage() " << groupUrl;

    // Loading of the URL in the request
    groupRequest.setUrl(groupUrl);
    // Set an additionnal attribute to recognise the goal of the request
    groupRequest.setAttribute(QNetworkRequest::User,RT_GroupRead);

    pReply = this->get(groupRequest);
}

void WebClient :: requestCompilationPage(const unsigned int id)
{
    // Creation of the URL
    QUrl compilationUrl(basicRequest.url().toString() +QString(COMPILATION_PATH) + QString("%1").arg(id) + QString("/"));
    QNetworkRequest compilationRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestCompilationPage() " << compilationUrl;

    // Loading of the URL in the request
    compilationRequest.setUrl(compilationUrl);
    // Set an additionnal attribute to recognise the goal of the request
    compilationRequest.setAttribute(QNetworkRequest::User,RT_CompilationRead);

    pReply = this->get(compilationRequest);
}

void WebClient :: requestUserPage(const QString username)
{
    // Creation of the URL
    QUrl userUrl(basicRequest.url().toString() +QString(USER_PATH) + username + QString("/"));
    QNetworkRequest userRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient requestUserPage() " << userUrl;

    // Loading of the URL in the request
    userRequest.setUrl(userUrl);
    // Set an additionnal attribute to recognise the goal of the request
    userRequest.setAttribute(QNetworkRequest::User,RT_UserRead);
    userRequest.setAttribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User+1),username);

    pReply = this->get(userRequest);
}

void WebClient :: login(const QString& username, const QString& password)
{
    // Creation of the URL
    QUrl loginUrl(basicRequest.url().toString() + QString(LOGIN_PATH));
    QNetworkRequest loginRequest(basicRequest);
    QByteArray upData;

    qDebug() << "WebClient :: login -> " << username << loginUrl;

    // Creation of the data stream to send
    // To log, we need the username and the password as follow:
    // username=toto&password=pass
    upData = upData.append("username=");
    upData = upData.append(username);
    upData = upData.append("&password=");
    upData = upData.append(password);

    loginRequest.setUrl(loginUrl);

    loginRequest.setHeader(QNetworkRequest::ContentLengthHeader, upData.size());
    loginRequest.setHeader(QNetworkRequest::ContentTypeHeader, TYPE_REQUEST);
    loginRequest.setRawHeader("Referer",LOGIN_REFERER);

    // Set an additionnal attribute to recognise the goal of the request
    loginRequest.setAttribute(QNetworkRequest::User,RT_Login);

    this->post(loginRequest,upData);
}

void WebClient :: requestVote(const unsigned int id, const short unsigned int vote)
{
    // Creation of the URL
    QUrl voteUrl(basicRequest.url().toString() + QString(SONG_REQUEST_PATH) + QString("%1/vote/%2/").arg(id).arg(vote));
    QNetworkRequest voteRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient :: requestVote -> " << id << "(" << vote << " Url: " << voteUrl << ")";

    // Loading of the URL in the request
    voteRequest.setUrl(voteUrl);
    // Set an additionnal attribute to recognise the goal of the request
    voteRequest.setAttribute(QNetworkRequest::User,RT_Vote);

    pReply = this->get(voteRequest);
}

void WebClient :: requestFavSong(const unsigned int id)
{
    // Creation of the URL
    QUrl favUrl(basicRequest.url().toString() + QString(ADD_FAV_PATH) + QString("%1/").arg(id));
    QNetworkRequest favRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient :: requestFavSong -> " << id << "(" << favUrl << ")";

    // Loading of the URL in the request
    favRequest.setUrl(favUrl);
    // Set an additionnal attribute to recognise the goal of the request
    favRequest.setAttribute(QNetworkRequest::User,RT_FavAdd);

    pReply = this->get(favRequest);
}

void WebClient :: requestUnFavSong(const unsigned int id)
{
    // Creation of the URL
    QUrl unfavUrl(basicRequest.url().toString() + QString(SUB_FAV_PATH) + QString("%1/").arg(id));
    QNetworkRequest unfavRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient :: requestUnFavSong -> " << id << "(" << unfavUrl << ")";

    // Loading of the URL in the request
    unfavRequest.setUrl(unfavUrl);
    // Set an additionnal attribute to recognise the goal of the request
    unfavRequest.setAttribute(QNetworkRequest::User,RT_UnFav);

    pReply = this->get(unfavRequest);
}

void WebClient :: requestRequestSong(const unsigned int id)
{
    // Creation of the URL
    QUrl requestUrl(basicRequest.url().toString() + QString(SONG_REQUEST_PATH) + QString("%1/queue/").arg(id));
    QNetworkRequest requestRequest(basicRequest);
    QNetworkReply* pReply = NULL;

    qDebug() << "WebClient :: requestUnFavSong -> " << id << "(" << requestUrl << ")";

    // Loading of the URL in the request
    requestRequest.setUrl(requestUrl);
    // Set an additionnal attribute to recognise the goal of the request
    requestRequest.setAttribute(QNetworkRequest::User,RT_SongRequest);

    pReply = this->get(requestRequest);
}

void WebClient :: sendOnelinerMessage(const QString& message)
{
    // Creation of the URL
    QUrl onelinerUrl(basicRequest.url().toString() + QString(ONELINER_SUBMIT_PATH));
    QNetworkRequest onelinerRequest(basicRequest);
    QByteArray upData;
    QString messageSent(message);

    // Replacing special characters for the oneliner (&, +)
    messageSent.replace('&', QString("%26"));
    messageSent.replace('+', QString("%2B"));
    messageSent.replace(';', QString("%3B"));

    qDebug() << "WebClient :: sendOnelinerMessage -> " << messageSent << onelinerUrl;

    // Creation of the data stream to send
    // Line=toto
    upData = upData.append("Line=");
    upData = upData.append(messageSent);

    onelinerRequest.setUrl(onelinerUrl);

    onelinerRequest.setHeader(QNetworkRequest::ContentLengthHeader, upData.size());
    // onelinerRequest.setHeader(QNetworkRequest::ContentTypeHeader, TYPE_REQUEST);

    // Set an additionnal attribute to recognise the goal of the request
    onelinerRequest.setAttribute(QNetworkRequest::User,RT_OnelinerSubmission);

    this->post(onelinerRequest,upData);
}

void WebClient :: replyFinished(QNetworkReply* pNReply)
{
    QNetworkRequest requestGetted = pNReply->request();
    RequestType requestType = (RequestType) requestGetted.attribute(QNetworkRequest::User,RT_Undefined).toInt();
    qDebug() << "WebClient replyFinished";

#ifdef QNDEBUG
    WebDebug::printInfoNetworkReply(pNReply);
#endif
    if ( pNReply->error() != 0 && requestType != RT_UnFav ) // The unfav URL is sending back wrong data
    {
        QString errorMessage = WebUtils::parseWebError(pNReply->error());
        qDebug() << "An error occured during the Web request (" << pNReply->error() << " :: " << errorMessage << " Request: " << requestType << ")" ;
        emit errorOccured(errorMessage);
    }
    else
    {
        switch ( requestType )
        {
        case RT_QueueRead:
            qDebug() << "RequestType: RT_QueueRead";
            queueData = pNReply->readAll();
            qDebug() << "Read:\n" << queueData << "\nEnd";
            emit queueReady();
            break;

        case RT_OnelinerRead:
            qDebug() << "RequestType: RT_OnelinerRead";
            onelinerData = pNReply->readAll();
            qDebug() << "Read:\n" << onelinerData << "\nEnd";
            emit onelinerReady();
            break;

        case RT_Login:
            qDebug() << "RequestType: RT_Login";
            if ( pNReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 302 )
            {
                qDebug() << "Connected";
                pCookies->setCookiesFromUrl(pNReply->header(QNetworkRequest::CookieHeader).value<QList<QNetworkCookie> >(),basicRequest.url());
                emit connectionSucceed();
            }
            else
            {
                qDebug() << "Fail to connect";
                emit connectionFailed();
            }
            break;

        case RT_NewsRead:
            qDebug() << "RequestType: RT_NewsRead";
            newsData = pNReply->readAll();
            qDebug() << "Read:\n" << newsData << "\nEnd";
            emit newsReady();
            break;
        case RT_UsersOnlineRead:
            qDebug() << "RequestType: RT_NewsRead";
            newsData = pNReply->readAll();
            qDebug() << "Read:\n" << newsData << "\nEnd";
            emit usersOnelineReady();
            break;
        case RT_InboxRead:
            qDebug() << "RequesTyme: RT_InboxRead";
            newsData = pNReply->readAll();
            qDebug() << "Read:\n" << newsData << "\nEnd";
            emit inboxReady();
            break;

        case RT_StreamRead:
            qDebug() << "RequestType: RT_StreamRead";
            streamData = pNReply->readAll();
            qDebug() << "Read:\n" << streamData << "\nEnd";
            emit streamReady();
            break;

        case RT_FavoritesRead:
            qDebug() << "RequestType: RT_FavoritesRead";
            favoritesData = pNReply->readAll();
            qDebug() << "Read:\n" << favoritesData << "\nEnd";
            emit favoritesReady();
            break;
        case RT_PaginatedFavoritesRead:
            qDebug() << "RequestType: RT_PaginatedFavoritesRead";
            favoritesData = pNReply->readAll();
            qDebug() << "Read:\n" << favoritesData << "\nEnd";
            emit paginatedFavoritesReady();
            break;

        case RT_UserFavoritesRead:
            qDebug() << "RequestType: RT_UserFavoritesRead";
            favoritesData = pNReply->readAll();
            qDebug() << "Read:\n" << favoritesData << "\nEnd";
            emit userFavoritesReady();
            break;
        case RT_UserPaginatedFavoritesRead:
            qDebug() << "RequestType: RT_UserPaginatedFavoritesRead";
            favoritesData = pNReply->readAll();
            qDebug() << "Read:\n" << favoritesData << "\nEnd";
            emit userPaginatedFavoritesReady();
            break;


        case RT_SongRead:
            qDebug() << "RequestType: RT_SongRead";
            songData = pNReply->readAll();
            qDebug() << "Read:\n" << songData << "\nEnd";
            emit songReady();
            break;

        case RT_ArtistRead:
            qDebug() << "RequestType: RT_ArtistRead";
            artistData = pNReply->readAll();
            qDebug() << "Read:\n" << artistData << "\nEnd";
            emit artistReady();
            break;

        case RT_GroupRead:
            qDebug() << "RequestType: RT_GroupRead";
            groupData = pNReply->readAll();
            qDebug() << "Read:\n" << groupData << "\nEnd";
            emit groupReady();
            break;

        case RT_CompilationRead:
            qDebug() << "RequestType: RT_CompilationRead";
            compilationData = pNReply->readAll();
            qDebug() << "Read:\n" << compilationData << "\nEnd";
            emit compilationReady();
            break;

        case RT_UserRead:
            {
                QString username = requestGetted.attribute(static_cast<QNetworkRequest::Attribute>(QNetworkRequest::User+1),"").toString();
                qDebug() << "RequestType: RT_UserRead (" << username << ")";
                userData = pNReply->readAll();
                qDebug() << "Read:\n" << userData << "\nEnd";
                emit userReady(username);
            }
            break;

        case RT_OnelinerSubmission:
            qDebug() << "RequestType: RT_OnelinerSubmission";
            qDebug() << "Read:\n" << pNReply->readAll() << "\nEnd";
            break;
        case RT_Vote:
            qDebug() << "RequestType: RT_Vote";
            qDebug() << "Read:\n" << pNReply->readAll() << "\nEnd";
            break;
        case RT_FavAdd:
            qDebug() << "RequestType: RT_FavAdd";
            qDebug() << "Read:\n" << pNReply->readAll() << "\nEnd";
            break;
        case RT_UnFav:
            qDebug() << "RequestType: RT_UnFav";
            qDebug() << "Read:\n" << pNReply->readAll() << "\nEnd";
            break;
        case RT_SongRequest:
            {
                QByteArray answerInfo = pNReply->readAll();
                qDebug() << "RequestType: RT_SongRequest";
                qDebug() << "Read:\n" << answerInfo << "\nEnd";
                if ( answerInfo.contains("Your Song Has Been Queued!") == false )
                {
                    // We had a problem
                    emit errorOccured("We received back something unexpected while requesting your song.");
                }
            }
            break;
        case RT_Undefined:
            qDebug() << "RequestType: RT_Undefined";
            break;
        }
    }

    pNReply->deleteLater();
}

void WebClient :: errorHandler(QNetworkReply::NetworkError errorCode)
{
    qDebug() << "WebClient :: errorHandler " << errorCode;
}

void WebClient :: defineProxy(const QNetworkProxy::ProxyType type, const QString& hostName, const quint16 port, const QString& user, const QString& password)
{
    qDebug() << "WebClient :: defineProxy (Host: " << hostName << " , User: " << user << " Port: " << port << ")";

    proxy = QNetworkProxy(type, hostName, port, user, password);

    this->setProxy(proxy);
}
